
package ca.etsmtl.logti.log619.lab05;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import ca.etsmtl.logti.log619.lab05.filter.EmailFilter;
import ca.etsmtl.utilities.ConsoleUtilities;
import ca.etsmtl.utilities.JarClassLoader;

/**
 * Class used to dynamically load the EmailFilters
 * 
 * @author Maxime Caumartin <maxime.caumartin.1@ens.etsmtl.ca>
 */
public class FilterFactory {
    /**
     * The directory that the filters will be loaded from.
     */
    private static String filterPath = "/filter/";

    /**
     * The HashMap containing the loaded filters.
     */
    private static HashMap<String, EmailFilter> filters = null;

    public static String getFilterPath() {
        return filterPath;
    }

    public static void setFilterPath(String path) {
        filterPath = path;
    }

    public static EmailFilter[] getFilters() {
        if (filters == null)
            loadFilters();
        if (FilterFactory.filters.size() != 0) {
            EmailFilter[] filters = new EmailFilter[FilterFactory.filters.size()];
            Collection<EmailFilter> vals = FilterFactory.filters.values();
            return vals.toArray(filters);
        }
        return new EmailFilter[0];
    }

    public static EmailFilter getFilter(String filterName) {
        return filters.get(filterName);
    }

    /**
     * Jar loading method from
     * "http://floris.ouwendijk.nl/blog/index.php?blog=2&title=jar_introspection&more=1&c=1&tb=1&pb=1"
     */
    private static void loadFilters() {
        filters = new HashMap<String, EmailFilter>(5);
        try {
            // Determine if we are loading from a jar or directly from the class
            URL u = Application.class.getProtectionDomain().getCodeSource().getLocation();
            if (u.toString().endsWith(".jar"))
                jarLoader(u);
            else {
                ConsoleUtilities.debug("Loading from classPath " + u);
                File d = new File(u.getPath().substring(0, u.getPath().lastIndexOf("/")).concat("/")
                        .concat(FilterFactory.class.getPackage().getName().replace(".", "/")).concat("/").concat(filterPath));

                if (d.exists())
                    for (String file : d.list())
                        if (file.endsWith(".jar"))
                            jarLoader(new URL("file", "", file));
                        else
                            addFile(file);
            }

            // Load all classes and jars in the "./filter" directory, if it exists
            File dir = new File(u.getPath() + filterPath);
            if (dir.exists())
                for (String file : dir.list())
                    if (file.endsWith(".jar"))
                        jarLoader(new URL("file", "", file));
                    else
                        addFile(file);

        } catch (IOException e) {
            System.err.println(e.getLocalizedMessage());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Filters through all the files in the jar URL passed in parameter, finds classes that implements EmailFilter, that
     * are in a @ref filterPath package
     * and creates an instance of that class.
     * 
     * @param u
     *            The URL defining the jar file
     * @throws URISyntaxException
     *             If a problem exists with the URL passed as parameter. Should not happen, however.
     */
    private static void jarLoader(URL u) throws URISyntaxException {
        ConsoleUtilities.debug("Loading from jar " + u.toString());
        // If we are in a jar file, check if there are filters inside!
        try {
            JarFile jf = new JarFile(new File(u.toURI()));

            Enumeration<JarEntry> e = jf.entries(); // List all files
            while (e.hasMoreElements()) {
                JarEntry entry = e.nextElement();
                if (entry.getName().contains(filterPath) && entry.getName().contains(".class"))
                    if (!entry.getName().contains("EmailFilter.class")) // The name of the interface. Explicit removal
                        if (!entry.getName().contains("$")) // Explicit removal of anonymous or internal classes
                            if (!filters.containsKey(entry.getName())) {
                                EmailFilter filter = (EmailFilter) new JarClassLoader(u).invokeClass(entry.getName().replace('/', '.')
                                        .substring(0, entry.getName().lastIndexOf('.'))); // Create the JarClassLoader
                                                                                          // and load the specified
                                                                                          // class
                                if (filter == null) // Class isn't implementing EmailFilter, continue...
                                    continue;
                                if (!filter.enabled())
                                    continue;
                                filters.put(filter.getFilterName(), filter);
                                ConsoleUtilities.debugP(
                                        ".",
                                        String.format("Loading class : %1$s",
                                                entry.getName().substring(0, entry.getName().indexOf(".class"))));
                            }
            }
            jf.close();
        } catch (IOException e) {
            return;
        }
    }

    /**
     * Adds a Object to the EmailFilter HashMap<EmailFilter>.
     * 
     * @param file
     *            The class file that will be added to the HashMap.
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     */
    private static void addFile(String file) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        if (!file.contains("EmailFilter.class")) // The name of the interface. Explicit removal
            if (!file.contains("$")) // Explicit removal of anonymous or internal classes
                if (!filters.containsKey(file) && !file.startsWith(".")) {
                    ConsoleUtilities.debug("Adding filter " + file);
                    EmailFilter filter = (EmailFilter) FilterFactory.class
                            .getClassLoader()
                            .loadClass(
                                    String.format("%1$s.filter.%2$s", FilterFactory.class.getPackage().getName(),
                                            file.substring(0, file.indexOf(".class")))).newInstance();
                    if (filter == null) // Class isn't implementing EmailFilter, return...
                        return;
                    if (!filter.enabled())
                        return;
                    filters.put(filter.getFilterName(), filter);
                    ConsoleUtilities.debugP(".", String.format("Loading class : %1$s", file.substring(0, file.indexOf(".class"))));
                }
    }
}
